package dormouse.vfs.ui;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.List;

import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;

import org.apache.commons.vfs2.FileFilter;
import org.apache.commons.vfs2.FileName;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSelectInfo;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.FileType;
import org.apache.commons.vfs2.Selectors;
import org.apache.commons.vfs2.VFS;

/**
 * This class provides a table model for files.
 */

public class FileListTableModel implements TableModel {

	/** A list of files. */
	private List<FileObject> fileList = new ArrayList<FileObject>();

	/** A list of selected files indexes. */
	private BitSet selectedList = new BitSet();

	/** The formatter for the file date. */
	private DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy HH:mm:ss");

	/** A list of listeners for the table. */
	private List<TableModelListener> listeners = new ArrayList<TableModelListener>();

	/**
	 * No args constructor.
	 */
	public FileListTableModel() {
		String home = System.getProperty("user.home");
		FileSystemManager fsManager;
		FileObject homeDir;
		try {
			fsManager = VFS.getManager();
			homeDir = fsManager.resolveFile(home);
			init(homeDir);
		} catch (FileSystemException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Constructor.
	 * 
	 * @param dir The directory to be displayed.
	 */
	public FileListTableModel(FileObject dir) {
		init(dir);
	}

	/**
	 * Constructor. Creates a FileListTableModel from an vector of files.
	 * 
	 * @param fileList
	 *            An array of files.
	 */
	public FileListTableModel(List<FileObject> fileList) {
		this.fileList = fileList;
		initSelected(this.fileList.size());
	}

	/**
	 * Constructor. Creates a table model from a list of files.
	 * 
	 * @param files
	 */
	public FileListTableModel(FileObject[] files) {
		initFileList(files);
		initSelected(files.length);
	}

	private void initSelected(int numFiles) {
		this.selectedList = new BitSet(numFiles);
	}

	/**
	 * This method initialises the file list table model using a directory. It
	 * fetches a list of files in a given directory and uses it to initialise
	 * the file list.
	 * 
	 * @param directory
	 *            The directory used to init the file list
	 */
	public void init(FileObject directory) {
		FileType type = null;
		try {
			type = directory.getType();
		} catch (FileSystemException e1) {
			e1.printStackTrace();
		}
		if (type != null && type == FileType.FOLDER) {
			FileObject[] origFiles = null;
			try {
				origFiles = directory.getChildren();
				initFileList(origFiles);
				initSelected(origFiles.length);
			} catch (FileSystemException e) {
				e.printStackTrace();
			}

		}
	}

	/**
	 * This method initialises the files list using a directory and a file
	 * filter.
	 * 
	 * @param directory
	 *            A directory from which to generate file list.
	 * @param filter
	 *            A filter to be applied to a directory file listing.
	 */
	public void init(FileObject directory, FileFilter filter) {
		FileType type = null;
		try {
			type = directory.getType();
		} catch (FileSystemException e1) {
			e1.printStackTrace();
		}

		if (type == FileType.FOLDER) {
			FileObject[] children = null;
			try {
				children = directory.getChildren();
				initFileList(children);
			} catch (FileSystemException e) {
				e.printStackTrace();
			}

		}
	}

	public void initFileList(FileObject[] files) {
		this.fileList = new ArrayList<FileObject>(files.length);
		this.fileList = Arrays.asList(files);
	}

	/**
	 * Gets the name of the selected column.
	 * 
	 * @param col
	 *            The column whose name you want.
	 */
	public String getColumnName(int col) {
		return colnames[col];
	}

	/**
	 * This method sorts the table by a given column
	 * 
	 * @param column
	 *            The index of the column you want to sort
	 * @param isAscending
	 *            A flag indicating whether to use ascending or descending sort
	 *            order
	 * 
	 * public void sortByColumn(int column, boolean isAscending) { if (sorter ==
	 * null) { sorter = new TableSorter(this); } sorter.sort(column,
	 * isAscending); fireTableDataChanged(); }
	 */

	/**
	 * This method sorts the table by a given column.
	 * 
	 * @param column
	 *            The index of the column you want to sort
	 * 
	 * public void sortByColumn(int column) { sortByColumn(column, true); }
	 */

	/**
	 * Gets the class of a given column.
	 * 
	 * @param col
	 *            The column whose class you want to determine.
	 */
	public Class getColumnClass(int col) {
		return colClasses[col];
	}

	/**
	 * Gets the value of a cell in the table.
	 * 
	 * @param row
	 *            The selected row.
	 * @param col
	 *            The selected column.
	 */
	public Object getValueAt(int row, int col) {
		Object val = null;
		FileObject selectedFile = (FileObject) fileList.get(row);
		FileName name = selectedFile.getName();
		FileType type = null;
		try {
			type = selectedFile.getType();
		} catch (FileSystemException e1) {
			e1.printStackTrace();
		}

		switch (col) {

		case NAME: {
			val = selectedFile.getName().getBaseName();
			break;
		}
		case TYPE: {
			if (type != null && type == FileType.FOLDER) {
				val = "File Folder";
			} else {
				val = name.getExtension();
			}
			break;
		}
		case SIZE: {
			try {
				if (type != null && type != FileType.FOLDER) {
					val = Long.valueOf(selectedFile.getContent().getSize());
				} else {
					val = null;
				}
			} catch (FileSystemException e) {
				e.printStackTrace();
			}
			break;
		}
		case MODIFIED: {
			try {
				val = dateFormat.format(selectedFile.getContent()
						.getLastModifiedTime());
			} catch (FileSystemException e) {
				e.printStackTrace();
			}
			break;
		}

		}
		return val;
	}

	/**
	 * This method sets the value of a particular cell.
	 * 
	 * @param value
	 *            The new value for the cell.
	 * @param rowIndex
	 *            The index of the row.
	 * @param columnIndex
	 *            The index of the column.
	 */
	public void setValueAt(Object value, int rowIndex, int columnIndex) {
		FileObject selectedFileObj = (FileObject) fileList.get(rowIndex);
		String dir = selectedFileObj.getName().getPath();

		switch (columnIndex) {
		case NAME: {
			try {

				String newName = (String) value;
				if (newName != null && !newName.equals("")) {
					String sep = System.getProperty("file.separator");
					newName = dir + sep + newName;

					rename(selectedFileObj, newName);
				}
			} catch (FileSystemException e) {
				e.printStackTrace();
			}
			break;
		}

		}
	}

	/**
	 * This method renames a file
	 * 
	 * @param sourceFileObject
	 * @param target
	 * @throws FileSystemException
	 */
	public static void rename(FileObject sourceFileObject, String target)
			throws FileSystemException {
		long lastModTime = sourceFileObject.getContent().getLastModifiedTime();
		FileSystemManager fsManager = VFS.getManager();
		FileObject targetFileObject = fsManager.resolveFile(target);
		if (!sourceFileObject.canRenameTo(targetFileObject)) { // other device
			targetFileObject.copyFrom(sourceFileObject, Selectors.SELECT_SELF);
			sourceFileObject.delete(Selectors.SELECT_SELF);
		} else { // same device
			sourceFileObject.moveTo(targetFileObject);
		}
		targetFileObject.getContent().setLastModifiedTime(lastModTime);
	}

	/**
	 * This method determines if the cell is editable.
	 * 
	 * @param rowIndex
	 *            The index of the row.
	 * @param columnIndex
	 *            The index of the column.
	 * @return A boolean value indicating whether or not the cell is editable.
	 */
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return (columnIndex == NAME);
	}

	/**
	 * This method gets the number of columns in the model.
	 * 
	 * @return An integer count of the number of columns in the model.
	 */
	public int getColumnCount() {
		return colnames.length;
	}

	/**
	 * This method gets a count of the number of files in the model.
	 * 
	 * @return An integer count of the number of files in the model
	 */
	public int getRowCount() {
		int count = 0;
		if (fileList != null) {
			count = fileList.size();
		}
		return count;
	}

	/**
	 * This method returns a filtered subset of the current files.
	 * 
	 * @param filter
	 *            The filter to be applied to the current model
	 * @return A filtered Vector of all of the files that meet the filtering
	 *         criteria.
	 */
	public List<FileObject> getFilteredList(FileFilter filter) {
		List<FileObject> files = new ArrayList<FileObject>();
		FileObject currFile = null;

		for (int i = 0; i < fileList.size(); i++) {
			currFile = (FileObject) fileList.get(i);
			if (filter.accept((FileSelectInfo) currFile)) {
				files.add(currFile);
			}
		}
		return files;
	}

	/**
	 * This method gets the file at the given row index.
	 * 
	 * @param rowIndex
	 *            A zero-based index of the files in FileListTableModel.
	 * @return
	 */
	public FileObject getFileAt(int rowIndex) {
            if (rowIndex >=0){
		return (FileObject) this.fileList.get(rowIndex);
            }else {
                return null;
            }
	}

	private static final String[] colnames = { "Name", "Type", "Size",
			"Modified" };

	private static final Class[] colClasses = { String.class, String.class,
			Long.class, String.class };

	private static final int NAME = 0;

	private static final int TYPE = 1;

	private static final int SIZE = 2;

	private static final int MODIFIED = 3;

	public void addTableModelListener(TableModelListener l) {
		listeners.add(l);
	}

	public void removeTableModelListener(TableModelListener l) {
		listeners.remove(l);

	}

}